<?php
/**
 ***********************************************************************************************************************
 * SOA服务器获取统一客户端基础类
 * @author llq
 ***********************************************************************************************************************
 */
namespace soa;
use artisan\http;

/**
 * Class client
 * @package soa\client
 */
abstract class client
{
    /**
     * 服务地址 请在具体子类里覆盖
     * 
     * @var string 
     */
    protected $service_url = 'http://core.kuaidihelp.com/';

    /**
     * 服务名称(核心接口项目名称) 请在具体子类里覆盖
     * 
     * @var string 
     */
    protected $service_name = 'kuaidihelp_core';

    /**
     * 服务版本号 请在子类里覆盖
     * 
     * @var string 
     */
    protected $service_version = 'v1';

    /**
     * 请求超时时间 请在具体子类里覆盖
     * 
     * @var string 
     */
    protected $_timeout = 10;

    /**
     * 服务返回或解析时产生的状态码 默认未知错误
     * 
     * @var int 
     */
    public static $_code = 1000;

    /**
     * 服务返回或解析时产生的错误的消息
     * 
     * @var string 
     */
    public static $_msg = '未知错误';

    /**
     * 解析时产生的错误码 默认0，表示成功
     * 
     * @var int
     */
    public static $_error = 0;

    /**
     * 接口返回原始数据
     * 
     * @var mixed
     */
    public static $_result = '';

    /**
     * 当前调用方法返回结果
     * 
     * @var string
     */
    public static $_data = '';

    /**
     * 响应内容
     * @var
     */
    protected $response;

    private $fullUrl='';

    /**
     * 设置请求服务超时时间
     *
     * @param int $second
     */
    public function setTimeout($second = 5)
    {
        $second = intval($second);
        $this->_timeout = $second < 5 ? 5 : $second;
    }

    /**
     * 获取服务地址
     *
     * @return string
     */
    public function getServiceUrl()
    {
        return $this->service_url;
    }

    /**
     * 获取服务项目名称
     *
     * @return string
     */
    public function getServiceName()
    {
        return $this->service_name;
    }

    /**
     * 获取服务版本号
     *
     * @return string
     */
    public function getServiceVersion()
    {
        return $this->service_version;
    }

    /**
     * 获取服务响应状态码
     *
     * @return int
     */
    public function getCode()
    {
        return self::$_code;
    }

    /**
     * 获取服务响应错误信息
     *
     * @return string
     */
    public function getMsg()
    {
        return self::$_msg;
    }

    /**
     * 获取服务响应原始内容
     *
     * @return mixed
     */
    public function getResult()
    {
        return self::$_result;
    }

    /**
     * 获取本次调用方法返回内容
     *
     * @return mixed
     */
    public function getData()
    {
        return self::$_data;
    }

    /**
     * 获取解析错误信息
     *
     * @return int
     */
    public function getError()
    {
        return self::$_error;
    }


    /**
     * GET 请求接口
     *
     * @param $api
     * @param array $data
     * @return array
     */
    protected function get($api, $data = [])
    {
        return $this->request($api, $data);
    }


    /**
     * POST 请求接口
     *
     * @param $api
     * @param array $data
     * @return array
     */
    protected function post($api, $data = [])
    {
        return $this->request($api, $data, 'POST');
    }

    /**
     * 自定义请求数据2 兼容代码 不要使用\
     *
     * @param $api
     * @param array $data
     * @return array
     */
    protected function request_params($api, $data)
    {
        return $data;
    }

    /**
     * 自定义请求API URL
     *
     * @param $api
     * @return string
     */
    protected function request_api($api)
    {
        $this->fullUrl=rtrim($this->service_url, '/') . '/' . ltrim($api, '/');
        return $this->fullUrl;
    }

    /**
     * @param $api
     * @param array $data
     * @param string $type
     * @return array
     */
    protected function request($api, $data = [], $type = 'GET')
    {
        // 请求参数检测
        if(empty($api) || empty($this->service_url) || empty($type)) {
            return [];
        }
        // 请求内容检测
        if(empty($data) && !is_array($data)) {
            $data = [];
        }

        $time1 = time();
        // 设置请求时间
        $option = [
            // 设置超时时间
            'TIMEOUT' => $this->_timeout,
            // 设置请求Header信息
            'HTTPHEADER' => [
                // 设置请求客户端名称 服务端容易记录请求者
                'client : ' . project(),
                'version : ' . $this->getServiceVersion()
            ]
        ];

        // 为自定义参数提供支持
        $data = $this->request_params($api, $data);
        $api_url = $this->request_api($api);

//        logDebug($api_url);
        // 去除DELETE\PUT等类型，直接GET\POST容易处理
        switch ($type) {
            case 'POST':
                $response = http::post($api_url, $data, $option);
                break;
            case 'GET':
            default:
                $response = http::get($api_url, $data, $option);
            break;
        }
        $this->response = $response;
		//logDebug($response,'lxd');
        // 解析返回结果
        $ret = $this->parse($response,'code','msg','data');

        // 设置状态码和消息
        self::$_msg = $ret->msg;
        self::$_code = $ret->code;
        self::$_error = $ret->error;

        // 错误判断 记录状态码小于1009的错误
        if($ret->error !== 0) {
            // 业务错误日志，取消记录
            if($this->getCode() < 1009 && (json_last_error() !== 0)) {
                $time2 = time();
                // 注：记录客户端名称（即项目名称）、错误消息、错误的接口（左侧不带/）、服务名称
                logError('[' . project() . ']' . $ret->msg . '耗时：'.($time2-$time1), ltrim($api, '/'), $this->service_name);
            }
        }
        // 设置并且返回结果
        return self::$_data = $ret->data;
    }

    /**
     * 解析restful api返回
     *
     * @param string $result
     * @param string $code
     * @param string $msg
     * @param string $data
     * @return mixed
     */
    protected function parse($result = '', $code = 'code', $msg = 'msg', $data = 'data')
    {
        // 重置响应结果
        $this->_reset($result);

        // 重置服务结果
        $json = json_decode('{"error":0,"msg":"成功","code":0}');
        
        // 初始化最终返回数据
        $json->data = '';

        // 返回数据为空 注：可能连接超时、网络错误、服务器错误
        if(empty($result)) {
            $json->error += 1;
            $json->msg = '网络忙，请稍后重试[该服务或接口无返回]!';
            // 不能设置500错误，为了兼容负载均衡服务器，如果开发者抛出太多500以上600以下错误，会导致该台服务器被下线
            $json->code = 1000;
            return $json;
        }

        // 返回数据格式错误 注：返回数据不合法
        $arr = json_decode($result, true);
        if(json_last_error() != 0) {
            //http::post('http://api.kuaidihelp.com/v1/MyTest/remoteLogFile',['message'=>['api'=>$this->fullUrl,'result'=>$result]],['TIMEOUT'=>1]);
            $json->error += 1;
            $json->msg = '该服务或接口返回结果无法解析:' . json_last_error() . (string)json_last_error_msg();
            $json->code = 422;
            return $json;
        }

        // 返回数据状态码错误 注：返回数据不合法
        if(!isset($arr[$code]) || $arr[$code] != 0) {
            $json->error += 1;
            $json->msg = !empty($arr[$msg]) ? $arr[$msg] : '该服务或接口返回未知状态错误!';
            $json->code = !isset($arr[$code]) ? 422 : intval($arr[$code]);
            // 如果存在data 进行覆盖
            if(isset($arr[$data])) {
                $json->data = $arr[$data];
            }
            return $json;
        }

        // 设置正确状态码
        $json->code = 0;
        if(isset($arr[$data])) {
            $json->data = $arr[$data];
        }
        return $json;
    }

    /**
     * 重置响应结果
     *
     * @param $result
     */
    protected function _reset($result = [])
    {
        self::$_result = $result;
        self::$_data = '';
        self::$_code = 1000;
        self::$_error = 0;
        self::$_msg = '未知错误';
    }
}